Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
7: In readChar(file, size, TRUE) : truncating string with embedded nuls
8: In readChar(file, size, TRUE) : truncating string with embedded nuls
9: In readChar(file, size, TRUE) : truncating string with embedded nuls
10: In readChar(file, size, TRUE) : truncating string with embedded nuls

Introduction

The purpose of this notebook is to give data locations, data ingestion code, and code for rudimentary analysis and visualization of COVID-19 data provided by New York Times, [NYT1].

The following steps are taken:

Note that other, older repositories with COVID-19 data exist, like, [JH1, VK1].

Remark: The time series section is done for illustration purposes only. The forecasts there should not be taken seriously.

Preliminary defintions

From the help of tolower:

capwords <- function(s, strict = FALSE) {
    cap <- function(s) paste(toupper(substring(s, 1, 1)),
                  {s <- substring(s, 2); if(strict) tolower(s) else s},
                             sep = "", collapse = " " )
    sapply(strsplit(s,  split = " "), cap, USE.NAMES = !is.null(names(s)))
}

Import data

NYTimes USA states data

if( !exists("dfNYDataStates") ) {
  dfNYDataStates <- read.csv( "https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv", 
                              colClasses = c("character", "character", "character", "integer", "integer"), 
                              stringsAsFactors = FALSE )
  colnames(dfNYDataStates) <- capwords(colnames(dfNYDataStates))
}
head(dfNYDataStates)
dfNYDataStates$DateObject <- as.POSIXct(dfNYDataStates$Date)
summary(as.data.frame(unclass(dfNYDataStates), stringsAsFactors = TRUE))
         Date                State           Fips          Cases            Deaths          DateObject                 
 2020-03-28:  55   Washington   : 127   53     : 127   Min.   :     1   Min.   :    0.0   Min.   :2020-01-21 00:00:00  
 2020-03-29:  55   Illinois     : 124   17     : 124   1st Qu.:   116   1st Qu.:    2.0   1st Qu.:2020-03-23 00:00:00  
 2020-03-30:  55   California   : 123   06     : 123   Median :  1638   Median :   44.0   Median :2020-04-14 00:00:00  
 2020-03-31:  55   Arizona      : 122   04     : 122   Mean   : 12235   Mean   :  680.6   Mean   :2020-04-12 17:08:18  
 2020-04-01:  55   Massachusetts: 116   25     : 116   3rd Qu.:  8942   3rd Qu.:  322.0   3rd Qu.:2020-05-05 00:00:00  
 2020-04-02:  55   Wisconsin    : 112   55     : 112   Max.   :368669   Max.   :29241.0   Max.   :2020-05-26 00:00:00  
 (Other)   :4359   (Other)      :3965   (Other):3965                                                                   

Summary by state:

by( data = as.data.frame(unclass(dfNYDataStates)), INDICES = dfNYDataStates$State, FUN = summary )

Alternative summary:

Hmisc::describe(dfNYDataStates)

NYTimes USA counties data

if(!exists("dfNYDataCounties") ) {
  dfNYDataCounties <- read.csv( "https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv", 
                                colClasses = c("character", "character", "character", "character", "integer", "integer"),
                                stringsAsFactors = FALSE )
  colnames(dfNYDataCounties) <- capwords(colnames(dfNYDataCounties))
}
head(dfNYDataCounties)
dfNYDataCounties$DateObject <- as.POSIXct(dfNYDataCounties$Date)
summary(as.data.frame(unclass(dfNYDataCounties)))
     Date              County             State               Fips               Cases              Deaths           DateObject                 
 Length:179696      Length:179696      Length:179696      Length:179696      Min.   :     0.0   Min.   :    0.00   Min.   :2020-01-21 00:00:00  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:     4.0   1st Qu.:    0.00   1st Qu.:2020-04-09 00:00:00  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :    16.0   Median :    0.00   Median :2020-04-26 00:00:00  
                                                                             Mean   :   319.3   Mean   :   17.75   Mean   :2020-04-24 12:34:45  
                                                                             3rd Qu.:    76.0   3rd Qu.:    2.00   3rd Qu.:2020-05-11 00:00:00  
                                                                             Max.   :204111.0   Max.   :20795.00   Max.   :2020-05-26 00:00:00  

US county records

if(!exists("dfUSACountyData")){
  dfUSACountyData <- read.csv( "https://raw.githubusercontent.com/antononcube/SystemModeling/master/Data/dfUSACountyRecords.csv", 
                               colClasses = c("character", "character", "character", "character", "integer", "numeric", "numeric"),
                               stringsAsFactors = FALSE )
}
head(dfUSACountyData)
summary(as.data.frame(unclass(dfUSACountyData), stringsAsFactors = TRUE))
         Country          State                   County          FIPS        Population            Lat             Lon         
 UnitedStates:3143   Texas   : 254   WashingtonCounty:  30   01001  :   1   Min.   :      89   Min.   :19.60   Min.   :-166.90  
                     Georgia : 159   JeffersonCounty :  25   01003  :   1   1st Qu.:   10980   1st Qu.:34.70   1st Qu.: -98.23  
                     Virginia: 134   FranklinCounty  :  24   01005  :   1   Median :   25690   Median :38.37   Median : -90.40  
                     Kentucky: 120   JacksonCounty   :  23   01007  :   1   Mean   :  102248   Mean   :38.46   Mean   : -92.28  
                     Missouri: 115   LincolnCounty   :  23   01009  :   1   3rd Qu.:   67507   3rd Qu.:41.81   3rd Qu.: -83.43  
                     Kansas  : 105   MadisonCounty   :  19   01011  :   1   Max.   :10170292   Max.   :69.30   Max.   : -67.63  
                     (Other) :2256   (Other)         :2999   (Other):3137                                                       

Merge data

dsNYDataCountiesExtended <- 
  dfNYDataCounties %>% 
  dplyr::inner_join( dfUSACountyData %>% dplyr::select_at( .vars = c("FIPS", "Lat", "Lon", "Population") ), by = c( "Fips" = "FIPS" ) )
dsNYDataCountiesExtended

Basic data analysis

ParetoPlotForColumns( dsNYDataCountiesExtended, c("Cases", "Deaths"), scales = "free" )

Geo-histogram

ggplot2

Note that in the plots in this sub-section we filter out Hawaii and Alaska.

ggplot2::ggplot(dsNYDataCountiesExtended[ dsNYDataCountiesExtended$Lon > -130, c("Lat", "Lon", "Cases")]) +
  ggplot2::geom_point( ggplot2::aes(x = Lon, y = Lat, fill = log10(Cases)), alpha = 0.01, size = 0.5, color = "blue" ) + 
  ggplot2::coord_quickmap()

Leaflet

cf <- colorBin( palette = "Reds", domain = log10(dsNYDataCountiesExtended$Cases), bins = 10 )
m <- 
  leaflet( dsNYDataCountiesExtended[, c("Lat", "Lon", "Cases")] ) %>%
  addTiles() %>% 
  addCircleMarkers( ~Lon, ~Lat, radius = ~ log10(Cases), fillColor = ~ cf(log10(Cases)), color = ~ cf(log10(Cases)), fillOpacity = 0.8, stroke = FALSE, popup = ~Cases )
n too large, allowed maximum for palette Reds is 9
Returning the palette you asked for with that many colors
n too large, allowed maximum for palette Reds is 9
Returning the palette you asked for with that many colors
m
dsNYDataCountiesExtended

Heat-map plots

An alternative of the geo-visualization is to use a heat-map plot.

Cases

Make a heat-map plot by sorting the rows of the cross-tabulation matrix (that correspond to states):

matSDC <- xtabs( Cases ~ State + Date, dfNYDataStates, sparse = TRUE)
d3heatmap::d3heatmap( log10(matSDC+1), cellnote = as.matrix(matSDC), scale = "none", dendrogram = "row", colors = "Blues") #, theme = "dark")

Deaths

Cross-tabulate states with dates over deaths and plot:

matSDD <- xtabs( Deaths ~ State + Date, dfNYDataStates, sparse = TRUE)
d3heatmap::d3heatmap( log10(matSDD+1), cellnote = as.matrix(matSDD), scale = "none", dendrogram = "row", colors = "Blues") #, theme = "dark")

Time series analysis

In this section we do simple “forecasting” (not a serious attempt).

Make time series data frame in long form:

dfQuery <- 
  dfNYDataStates %>% 
  dplyr::group_by( Date, DateObject ) %>% 
  dplyr::summarise_at( .vars = c("Cases", "Deaths"), .funs = sum )
dfQueryLongForm <- tidyr::pivot_longer( dfQuery, cols = c("Cases", "Deaths"), names_to = "Variable", values_to = "Value")
head(dfQueryLongForm)

Plot the time series:

ggplot(dfQueryLongForm) +
  geom_line( aes( x = DateObject, y = log10(Value) ) ) +
  facet_wrap( ~Variable, ncol = 1 )

Cases

Fit using ARIMA:

fit <- forecast::auto.arima( dfQuery$Cases )
fit
Series: dfQuery$Cases 
ARIMA(2,2,2) 

Coefficients:
         ar1      ar2      ma1     ma2
      1.2551  -0.9650  -1.2973  0.8625
s.e.  0.0260   0.0261   0.0904  0.0496

sigma^2 estimated as 3615446:  log likelihood=-1120.17
AIC=2250.33   AICc=2250.84   BIC=2264.47

Plot “forecast”:

plot( forecast::forecast(fit, h = 20) )
grid(nx = NULL, ny = NULL, col = "lightgray", lty = "dotted")

Deaths

Fit with ARIMA:

fit <- forecast::auto.arima( dfQuery$Deaths )
fit
Series: dfQuery$Deaths 
ARIMA(2,2,2) 

Coefficients:
         ar1      ar2      ma1      ma2
      0.2726  -0.2250  -0.0100  -0.4164
s.e.  0.1328   0.1192   0.1164   0.0948

sigma^2 estimated as 53413:  log likelihood=-856.12
AIC=1722.23   AICc=1722.74   BIC=1736.37

Plot “forecast”:

plot( forecast::forecast(fit, h = 20) )
grid(nx = NULL, ny = NULL, col = "lightgray", lty = "dotted")

LS0tCnRpdGxlOiAiTmV3IFlvcmsgVGltZXMgQ09WSUQtMTkgZGF0YSB2aXN1YWxpemF0aW9uIgphdXRob3I6IEFudG9uIEFudG9ub3YKZGF0ZTogMjAyMC0wMy0zMApvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfd2lkdGg6IDYKICAgIGZpZ19oaWdodDogNAogICAgZmlnX2FsaWduOiAiY2VudGVyIgotLS0KCmBgYHtyLCBlY2hvPUZBTFNFfQpsaWJyYXJ5KEhtaXNjKQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShkM2hlYXRtYXApCmxpYnJhcnkoUGFyZXRvUHJpbmNpcGxlQWRoZXJlbmNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShmb3JlY2FzdCkKYGBgCgojIEludHJvZHVjdGlvbgoKVGhlIHB1cnBvc2Ugb2YgdGhpcyBub3RlYm9vayBpcyB0byBnaXZlIGRhdGEgbG9jYXRpb25zLCBkYXRhIGluZ2VzdGlvbiBjb2RlLCBhbmQgY29kZSBmb3IgcnVkaW1lbnRhcnkgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24gb2YgQ09WSUQtMTkgZGF0YSBwcm92aWRlZCBieSBOZXcgWW9yayBUaW1lcywgW05ZVDFdLiAKClRoZSBmb2xsb3dpbmcgc3RlcHMgYXJlIHRha2VuOgoKLSBJbmdlc3QgZGF0YQoKICAtIFRha2UgQ09WSUQtMTkgZGF0YSBmcm9tIFRoZSBOZXcgWW9yayBUaW1lcywgYmFzZWQgb24gcmVwb3J0cyBmcm9tIHN0YXRlIGFuZCBsb2NhbCBoZWFsdGggYWdlbmNpZXMsIFtOWVQxXS4KCiAgLSBUYWtlIFVTQSBjb3VudGllcyByZWNvcmRzIGRhdGEgKEZJUFMgY29kZXMsIGdlby1jb29yZGluYXRlcywgcG9wdWxhdGlvbnMpLCBbV1JJMV0uCgotIE1lcmdlIHRoZSBkYXRhLgoKLSBNYWtlIGRhdGEgc3VtbWFyaWVzIGFuZCByZWxhdGVkIHBsb3RzLgoKLSBNYWtlIGNvcnJlc3BvbmRpbmcgZ2VvLXBsb3RzLgoKTm90ZSB0aGF0IG90aGVyLCBvbGRlciByZXBvc2l0b3JpZXMgd2l0aCBDT1ZJRC0xOSBkYXRhIGV4aXN0LCBsaWtlLCBbSkgxLCBWSzFdLgoKKipSZW1hcms6KiogVGhlIHRpbWUgc2VyaWVzIHNlY3Rpb24gaXMgZG9uZSBmb3IgaWxsdXN0cmF0aW9uIHB1cnBvc2VzIG9ubHkuIFRoZSBmb3JlY2FzdHMgdGhlcmUgc2hvdWxkIG5vdCBiZSB0YWtlbiBzZXJpb3VzbHkuCgojIFByZWxpbWluYXJ5IGRlZmludGlvbnMKCkZyb20gdGhlIGhlbHAgb2YgYHRvbG93ZXJgOgoKYGBge3J9CmNhcHdvcmRzIDwtIGZ1bmN0aW9uKHMsIHN0cmljdCA9IEZBTFNFKSB7CiAgICBjYXAgPC0gZnVuY3Rpb24ocykgcGFzdGUodG91cHBlcihzdWJzdHJpbmcocywgMSwgMSkpLAogICAgICAgICAgICAgICAgICB7cyA8LSBzdWJzdHJpbmcocywgMik7IGlmKHN0cmljdCkgdG9sb3dlcihzKSBlbHNlIHN9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIiLCBjb2xsYXBzZSA9ICIgIiApCiAgICBzYXBwbHkoc3Ryc3BsaXQocywgIHNwbGl0ID0gIiAiKSwgY2FwLCBVU0UuTkFNRVMgPSAhaXMubnVsbChuYW1lcyhzKSkpCn0KYGBgCgojIEltcG9ydCBkYXRhCgojIyBOWVRpbWVzIFVTQSBzdGF0ZXMgZGF0YQoKYGBge3J9CmlmKCAhZXhpc3RzKCJkZk5ZRGF0YVN0YXRlcyIpICkgewogIGRmTllEYXRhU3RhdGVzIDwtIHJlYWQuY3N2KCAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL255dGltZXMvY292aWQtMTktZGF0YS9tYXN0ZXIvdXMtc3RhdGVzLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xDbGFzc2VzID0gYygiY2hhcmFjdGVyIiwgImNoYXJhY3RlciIsICJjaGFyYWN0ZXIiLCAiaW50ZWdlciIsICJpbnRlZ2VyIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UgKQogIGNvbG5hbWVzKGRmTllEYXRhU3RhdGVzKSA8LSBjYXB3b3Jkcyhjb2xuYW1lcyhkZk5ZRGF0YVN0YXRlcykpCn0KaGVhZChkZk5ZRGF0YVN0YXRlcykKYGBgCgpgYGB7cn0KZGZOWURhdGFTdGF0ZXMkRGF0ZU9iamVjdCA8LSBhcy5QT1NJWGN0KGRmTllEYXRhU3RhdGVzJERhdGUpCmBgYAoKYGBge3J9CnN1bW1hcnkoYXMuZGF0YS5mcmFtZSh1bmNsYXNzKGRmTllEYXRhU3RhdGVzKSwgc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpKQpgYGAKClN1bW1hcnkgYnkgc3RhdGU6CgpgYGB7ciwgZXZhbD1GQUxTRX0KYnkoIGRhdGEgPSBhcy5kYXRhLmZyYW1lKHVuY2xhc3MoZGZOWURhdGFTdGF0ZXMpKSwgSU5ESUNFUyA9IGRmTllEYXRhU3RhdGVzJFN0YXRlLCBGVU4gPSBzdW1tYXJ5ICkKYGBgCgpBbHRlcm5hdGl2ZSBzdW1tYXJ5OgoKYGBge3IsIGV2YWw9RkFMU0V9CkhtaXNjOjpkZXNjcmliZShkZk5ZRGF0YVN0YXRlcykKYGBgCgoKIyMgTllUaW1lcyBVU0EgY291bnRpZXMgZGF0YQoKYGBge3J9CmlmKCFleGlzdHMoImRmTllEYXRhQ291bnRpZXMiKSApIHsKICBkZk5ZRGF0YUNvdW50aWVzIDwtIHJlYWQuY3N2KCAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL255dGltZXMvY292aWQtMTktZGF0YS9tYXN0ZXIvdXMtY291bnRpZXMuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sQ2xhc3NlcyA9IGMoImNoYXJhY3RlciIsICJjaGFyYWN0ZXIiLCAiY2hhcmFjdGVyIiwgImNoYXJhY3RlciIsICJpbnRlZ2VyIiwgImludGVnZXIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UgKQogIGNvbG5hbWVzKGRmTllEYXRhQ291bnRpZXMpIDwtIGNhcHdvcmRzKGNvbG5hbWVzKGRmTllEYXRhQ291bnRpZXMpKQp9CmhlYWQoZGZOWURhdGFDb3VudGllcykKYGBgCgpgYGB7cn0KZGZOWURhdGFDb3VudGllcyREYXRlT2JqZWN0IDwtIGFzLlBPU0lYY3QoZGZOWURhdGFDb3VudGllcyREYXRlKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGFzLmRhdGEuZnJhbWUodW5jbGFzcyhkZk5ZRGF0YUNvdW50aWVzKSkpCmBgYAoKIyMgVVMgY291bnR5IHJlY29yZHMKCmBgYHtyfQppZighZXhpc3RzKCJkZlVTQUNvdW50eURhdGEiKSl7CiAgZGZVU0FDb3VudHlEYXRhIDwtIHJlYWQuY3N2KCAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FudG9ub25jdWJlL1N5c3RlbU1vZGVsaW5nL21hc3Rlci9EYXRhL2RmVVNBQ291bnR5UmVjb3Jkcy5jc3YiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbENsYXNzZXMgPSBjKCJjaGFyYWN0ZXIiLCAiY2hhcmFjdGVyIiwgImNoYXJhY3RlciIsICJjaGFyYWN0ZXIiLCAiaW50ZWdlciIsICJudW1lcmljIiwgIm51bWVyaWMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSApCn0KaGVhZChkZlVTQUNvdW50eURhdGEpCmBgYAoKYGBge3J9CnN1bW1hcnkoYXMuZGF0YS5mcmFtZSh1bmNsYXNzKGRmVVNBQ291bnR5RGF0YSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBUUlVFKSkKYGBgCgojIE1lcmdlIGRhdGEKCmBgYHtyfQpkc05ZRGF0YUNvdW50aWVzRXh0ZW5kZWQgPC0gCiAgZGZOWURhdGFDb3VudGllcyAlPiUgCiAgZHBseXI6OmlubmVyX2pvaW4oIGRmVVNBQ291bnR5RGF0YSAlPiUgZHBseXI6OnNlbGVjdF9hdCggLnZhcnMgPSBjKCJGSVBTIiwgIkxhdCIsICJMb24iLCAiUG9wdWxhdGlvbiIpICksIGJ5ID0gYyggIkZpcHMiID0gIkZJUFMiICkgKQpkc05ZRGF0YUNvdW50aWVzRXh0ZW5kZWQKYGBgCgoKIyBCYXNpYyBkYXRhIGFuYWx5c2lzCgpgYGB7cn0KUGFyZXRvUGxvdEZvckNvbHVtbnMoIGRzTllEYXRhQ291bnRpZXNFeHRlbmRlZCwgYygiQ2FzZXMiLCAiRGVhdGhzIiksIHNjYWxlcyA9ICJmcmVlIiApCmBgYAoKIyBHZW8taGlzdG9ncmFtCgojIyBnZ3Bsb3QyCgpOb3RlIHRoYXQgaW4gdGhlIHBsb3RzIGluIHRoaXMgc3ViLXNlY3Rpb24gd2UgZmlsdGVyIG91dCBIYXdhaWkgYW5kIEFsYXNrYS4KCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QoZHNOWURhdGFDb3VudGllc0V4dGVuZGVkWyBkc05ZRGF0YUNvdW50aWVzRXh0ZW5kZWQkTG9uID4gLTEzMCwgYygiTGF0IiwgIkxvbiIsICJDYXNlcyIpXSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoIGdncGxvdDI6OmFlcyh4ID0gTG9uLCB5ID0gTGF0LCBmaWxsID0gbG9nMTAoQ2FzZXMpKSwgYWxwaGEgPSAwLjAxLCBzaXplID0gMC41LCBjb2xvciA9ICJibHVlIiApICsgCiAgZ2dwbG90Mjo6Y29vcmRfcXVpY2ttYXAoKQpgYGAKCiMjIExlYWZsZXQKCmBgYHtyfQpjZiA8LSBjb2xvckJpbiggcGFsZXR0ZSA9ICJSZWRzIiwgZG9tYWluID0gbG9nMTAoZHNOWURhdGFDb3VudGllc0V4dGVuZGVkJENhc2VzKSwgYmlucyA9IDEwICkKYGBgCgpgYGB7cn0KbSA8LSAKICBsZWFmbGV0KCBkc05ZRGF0YUNvdW50aWVzRXh0ZW5kZWRbLCBjKCJMYXQiLCAiTG9uIiwgIkNhc2VzIildICkgJT4lCiAgYWRkVGlsZXMoKSAlPiUgCiAgYWRkQ2lyY2xlTWFya2VycyggfkxvbiwgfkxhdCwgcmFkaXVzID0gfiBsb2cxMChDYXNlcyksIGZpbGxDb2xvciA9IH4gY2YobG9nMTAoQ2FzZXMpKSwgY29sb3IgPSB+IGNmKGxvZzEwKENhc2VzKSksIGZpbGxPcGFjaXR5ID0gMC44LCBzdHJva2UgPSBGQUxTRSwgcG9wdXAgPSB+Q2FzZXMgKQptCmBgYAoKYGBge3J9CmRzTllEYXRhQ291bnRpZXNFeHRlbmRlZApgYGAKCiMgSGVhdC1tYXAgcGxvdHMKCkFuIGFsdGVybmF0aXZlIG9mIHRoZSBnZW8tdmlzdWFsaXphdGlvbiBpcyB0byB1c2UgYSBoZWF0LW1hcCBwbG90LgoKCiMjIENhc2VzCgpNYWtlIGEgaGVhdC1tYXAgcGxvdCBieSBzb3J0aW5nIHRoZSByb3dzIG9mIHRoZSBjcm9zcy10YWJ1bGF0aW9uIG1hdHJpeCAodGhhdCBjb3JyZXNwb25kIHRvIHN0YXRlcyk6CgpgYGB7cn0KbWF0U0RDIDwtIHh0YWJzKCBDYXNlcyB+IFN0YXRlICsgRGF0ZSwgZGZOWURhdGFTdGF0ZXMsIHNwYXJzZSA9IFRSVUUpCmQzaGVhdG1hcDo6ZDNoZWF0bWFwKCBsb2cxMChtYXRTREMrMSksIGNlbGxub3RlID0gYXMubWF0cml4KG1hdFNEQyksIHNjYWxlID0gIm5vbmUiLCBkZW5kcm9ncmFtID0gInJvdyIsIGNvbG9ycyA9ICJCbHVlcyIpICMsIHRoZW1lID0gImRhcmsiKQpgYGAKCgojIyBEZWF0aHMKCkNyb3NzLXRhYnVsYXRlIHN0YXRlcyB3aXRoIGRhdGVzIG92ZXIgZGVhdGhzIGFuZCBwbG90OgoKCmBgYHtyfQptYXRTREQgPC0geHRhYnMoIERlYXRocyB+IFN0YXRlICsgRGF0ZSwgZGZOWURhdGFTdGF0ZXMsIHNwYXJzZSA9IFRSVUUpCmQzaGVhdG1hcDo6ZDNoZWF0bWFwKCBsb2cxMChtYXRTREQrMSksIGNlbGxub3RlID0gYXMubWF0cml4KG1hdFNERCksIHNjYWxlID0gIm5vbmUiLCBkZW5kcm9ncmFtID0gInJvdyIsIGNvbG9ycyA9ICJCbHVlcyIpICMsIHRoZW1lID0gImRhcmsiKQpgYGAKCiMgVGltZSBzZXJpZXMgYW5hbHlzaXMKCkluIHRoaXMgc2VjdGlvbiB3ZSBkbyBzaW1wbGUgImZvcmVjYXN0aW5nIiAobm90IGEgc2VyaW91cyBhdHRlbXB0KS4KCk1ha2UgdGltZSBzZXJpZXMgZGF0YSBmcmFtZSBpbiBsb25nIGZvcm06CgpgYGB7cn0KZGZRdWVyeSA8LSAKICBkZk5ZRGF0YVN0YXRlcyAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KCBEYXRlLCBEYXRlT2JqZWN0ICkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2VfYXQoIC52YXJzID0gYygiQ2FzZXMiLCAiRGVhdGhzIiksIC5mdW5zID0gc3VtICkKZGZRdWVyeUxvbmdGb3JtIDwtIHRpZHlyOjpwaXZvdF9sb25nZXIoIGRmUXVlcnksIGNvbHMgPSBjKCJDYXNlcyIsICJEZWF0aHMiKSwgbmFtZXNfdG8gPSAiVmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAiVmFsdWUiKQpoZWFkKGRmUXVlcnlMb25nRm9ybSkKYGBgCgpQbG90IHRoZSB0aW1lIHNlcmllczoKCmBgYHtyfQpnZ3Bsb3QoZGZRdWVyeUxvbmdGb3JtKSArCiAgZ2VvbV9saW5lKCBhZXMoIHggPSBEYXRlT2JqZWN0LCB5ID0gbG9nMTAoVmFsdWUpICkgKSArCiAgZmFjZXRfd3JhcCggflZhcmlhYmxlLCBuY29sID0gMSApCmBgYAoKIyMgQ2FzZXMKCkZpdCB1c2luZyBBUklNQToKCmBgYHtyfQpmaXQgPC0gZm9yZWNhc3Q6OmF1dG8uYXJpbWEoIGRmUXVlcnkkQ2FzZXMgKQpmaXQKYGBgCgpQbG90ICJmb3JlY2FzdCI6CgpgYGB7cn0KcGxvdCggZm9yZWNhc3Q6OmZvcmVjYXN0KGZpdCwgaCA9IDIwKSApCmdyaWQobnggPSBOVUxMLCBueSA9IE5VTEwsIGNvbCA9ICJsaWdodGdyYXkiLCBsdHkgPSAiZG90dGVkIikKYGBgCgojIyBEZWF0aHMKCkZpdCB3aXRoIEFSSU1BOgoKYGBge3J9CmZpdCA8LSBmb3JlY2FzdDo6YXV0by5hcmltYSggZGZRdWVyeSREZWF0aHMgKQpmaXQKYGBgCgpQbG90ICJmb3JlY2FzdCI6CgpgYGB7cn0KcGxvdCggZm9yZWNhc3Q6OmZvcmVjYXN0KGZpdCwgaCA9IDIwKSApCmdyaWQobnggPSBOVUxMLCBueSA9IE5VTEwsIGNvbCA9ICJsaWdodGdyYXkiLCBsdHkgPSAiZG90dGVkIikKYGBg